﻿using MQTTnet.Client;
using MQTTnet.Protocol;
using MQTTnet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpTest
{
    /// <summary>
    /// MQT客户端帮助类 
    /// </summary>
    public class MQTClientHelper
    {
    private IMqttClient _client;
    /// <summary> 
    /// 接收消息 
    /// </summary> 
    public event EventHandler<MqttApplicationMessageReceivedEventArgs> MessageReceived;

    public bool IsConected { get; set; } = false;
    public bool IsDisConected { get; set; } = true;
    private string _serverIp; private int _serverPort;
    /// <summary>
    /// 订阅主题集合
    /// </summary>
    private Dictionary<string, bool> _subscribeTopicList = null;
    #region 连接/断开服务端     
    /// <summary>
    /// 连接服务端
    /// </summary>
    /// <param name="serverIp">服务端ip</param>
    /// <param name="serverPort">服务端端口</param>
    public void Start(string serverIp, int serverPort)
    {
        this._serverIp = serverIp;
        this._serverPort = serverPort;
        if (!string.IsNullOrEmpty(serverIp) && !string.IsNullOrWhiteSpace(serverIp) && serverPort > 0)
        {
            try
            {
                var options = new MqttClientOptions()
                {
                    ClientId = Guid.NewGuid().ToString("N"),
                };
                options.ChannelOptions = new MqttClientTcpOptions()
                {
                    Server = serverIp,
                    Port = serverPort
                };
                options.Credentials = new MqttClientCredentials("lll", Encoding.Default.GetBytes(""));
                options.CleanSession = true;
                options.KeepAlivePeriod = TimeSpan.FromSeconds(10);
                if (_client != null)
                {
                    _client.DisconnectAsync();
                    _client = null;
                }
                _client = new MqttFactory().CreateMqttClient();
                _client.ConnectedAsync += Client_ConectedAsync; //绑定客户端连接事件             
                _client.DisconnectedAsync += Client_DisconectedAsync; //绑定客户端断开连接事件 
                _client.ApplicationMessageReceivedAsync += Client_AplicationMesageReceivedAsync; // 绑定消息接收事件
                _client.ConnectAsync(options); //连接 
            }
            catch (Exception ex)
            {
            }
        }
        else
        {
        }
    }


    /// <summary> 
    /// 断开MQT客户端 
    /// </summary> 
    public void Client_Disconect()
    {
        if (_client != null)
        {
            _client.DisconnectAsync();
            _client.Dispose();
            Console.WriteLine($"关闭MQT客户端成功！");
        }
    }
    /// <summary> 
    /// 客户端重新MQT服务端 
    /// </summary> 
    public void Client_ConectAsync()
    {
        if (_client != null)
        {
            _client.ReconnectAsync();
            Console.WriteLine($"连接MQT服务端成功！");
        }
    }
    #endregion
    #region MQT方法 
    /// <summary> 
    /// 客户端与服务端建立连接 
    /// </summary> 
    /// <param name="arg"></param> 
    private Task Client_ConectedAsync(MqttClientConnectedEventArgs arg)
    {
        return Task.Run(new Action(() => 
            {
            IsConected = true;
            IsDisConected = false;
            Console.WriteLine($"连接到MQT服务端成功.{arg.ConnectResult.AssignedClientIdentifier}");
            //订阅主题（可接收来自服务端消息，与客户端发布消息不能用同一个主题） 
            try
            {
                if (_subscribeTopicList != null && _subscribeTopicList.Count > 0)
                {
                    List<string> subscribeTopics = _subscribeTopicList.Keys.ToList();
                    foreach (var topic in subscribeTopics)
                        SubscribeAsync(topic);
                }
            }
            catch (Exception ex)
            {
                //SLog.Loger.Eror("MQT客户端与服务端[{0}:{1}]建立连接订阅主题错误：{2}", _serverIp, _serverPort, ex.Mesage); 
            }
        }));
    }

    /// <summary> 
    /// 客户端与服务端断开连接 
    /// </summary> / <param name="arg"></param> 
    private Task Client_DisconectedAsync(MqttClientDisconnectedEventArgs arg)
    {
        return Task.Run(new Action(async () =>
        {
            IsConected = false;
            IsDisConected = true;
            Console.WriteLine($"已断开到MQT服务端的连接.尝试重新连接");
            try
            {
                await Task.Delay(30);
                //MqtClientOptions options = new MqtClientOptions(); 
                //await mqtClient.ConectAsync(options); 
                await _client.ReconnectAsync();
            }
            catch (Exception ex)
            {
                //SLog.Loger.Eror("MQT客户端与服务端[{0}:{1}]断开连接退订主题错误：{2}", _serverIp, _serverPort, ex.Mesage); 
            }
        }));
    }
    /// <summary> 
    /// 客户端与服务端重新连接 
    /// </summary> 
    /// <returns></returns> 
    public Task ReconectedAsync()
    {
        try
        {
            if (_client != null)
            {
                _client.ReconnectAsync();
            }
        }
        catch (Exception ex)
        {
            // SLog.Loger.Eror("MQT客户端与服务端[{0}:{1}]重新连接退订主题错误：{2}", _serverIp, _serverPort, ex.Mesage); 
        }
        return Task.CompletedTask;
    }
    /// <summary> 
    /// 客户端收到消息 
    /// </summary> 
    /// <param name="arg"></param> 
    private Task Client_AplicationMesageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
    {
        try
        {
            return Task.Run(new Action(() => 
                {
                string msg = arg.ApplicationMessage.ConvertPayloadToString();
                Console.WriteLine($"接收消息：{msg}\nQoS={arg.ApplicationMessage.QualityOfServiceLevel}\n客户端={arg.ClientId}\n主题：{arg.ApplicationMessage.Topic}");
                MessageReceived?.Invoke(this, arg);
                }
                ));
        }
        catch (Exception ex)
        {
            //SLog.Loger.Eror("MQT收到来自服务端[{0}]消息错误：{1}", arg != nul ? arg.ClientId : ", ex.Mesage); 
        }
        return Task.CompletedTask;
    }
    #endregion
    #region 订阅主题 
    /// <summary> 
    /// 订阅主题 
    /// </summary> 
    /// <param name="topic">主题</param> 
    public void SubscribeAsync(string topic)
    {
        try
        {
            if (_subscribeTopicList == null)
                _subscribeTopicList = new Dictionary<string, bool>();
            if (_subscribeTopicList.ContainsKey(topic) && _subscribeTopicList[topic])
            {
                //SLog.Loger.Warning("MQT客户端已经订阅主题[{0}]，不能重复订阅", topic);     
                return;
            }
            //订阅主题 
            _client?.SubscribeAsync(topic, MqttQualityOfServiceLevel.AtLeastOnce);
            //添加订阅缓存 
            bool isSubscribed = _client != null && _client.IsConnected ? true : false;
            if (!_subscribeTopicList.ContainsKey(topic))
                    _subscribeTopicList.Add(topic, isSubscribed);
            else
                _subscribeTopicList[topic] = isSubscribed;
        }
        catch (Exception ex)
        {
            //SLog.Loger.Eror("MQT客户端订阅主题[{0}]错误：{1}", topic, ex.Mesage); 
        }
    }

    /// <summary> 
    /// 订阅主题集合 
    /// </summary> 
    /// <param name="topicList">主题集合</param> 
    public void SubscribeAsync(List<string> topicList)
    {
        try
        {
            if (topicList == null || topicList.Count == 0)
                return;
            foreach (var topic in topicList)
                SubscribeAsync(topic);
        }
        catch (Exception ex)
        {
            //SLog.Loger.Eror("MQT客户端订阅主题集合错误：{0}", ex.Mesage); 
        }
    }
    /// <summary> 
    /// 退订主题 
    /// </summary> 
    /// <param name="topic">主题</param> 
    /// <param name="isRemove">是否移除缓存</param> 
    public void UnsubscribeAsync(string topic, bool isRemove = true)
    {
        try
        {
            if (_subscribeTopicList == null || _subscribeTopicList.Count == 0)
            {
                //SLog.Loger.Warning("MQT客户端退订主题[{0}]不存在", topic); 
                return;
            }
            if (!_subscribeTopicList.ContainsKey(topic))
            {
                //SLog.Loger.Warning("MQT客户端退订主题[{0}]不存在", topic); 
                return;
            }
            //退订主题 
            _client.UnsubscribeAsync(topic);
            //修改订阅主题缓存状态 
            if (isRemove)
                _subscribeTopicList.Remove(topic);
            else
                _subscribeTopicList[topic] = false;
        }
        catch (Exception ex)
        {
            //SLog.Loger.Eror("MQT客户端退订主题[{0}]错误：{1}", topic, ex.Mesage); 
        }
    }
    /// <summary> 
    /// 退订主题集合 
    /// </summary> 
    /// <param name="topicList">主题集合</param> 
    /// <param name="isRemove">是否移除缓存</param> 
    public void UnsubscribeAsync(List<string> topicList, bool isRemove = true)
    {
        try
        {
            if (topicList == null || topicList.Count == 0)
                return;
            foreach (var topic in topicList)
                UnsubscribeAsync(topic, isRemove);
        }
        catch (Exception ex)
        {
            //SLog.Loger.Eror("MQT客户端退订主题集合错误：{0}", ex.Mesage); 
        }
    }
    /// <summary> 
    /// 订阅主题是否存在 
    /// </summary> 
    /// <param name="topic">主题</param> 
    public bool IsExistSubscribeAsync(string topic)
    {
        try
        {
            if (_subscribeTopicList == null || _subscribeTopicList.Count == 0)
                return false;
            if (!_subscribeTopicList.ContainsKey(topic))
                return false;
            return _subscribeTopicList[topic];
        }
        catch (Exception ex)
        {
            return false;
        }
    }
    #endregion
    #region 发布消息 
    /// <summary> 
    /// 发布消息 
    /// 与客户端接收消息不能用同一个主题 
    /// </summary> 
    /// <param name="topic">主题</param> 
    /// <param name="mesage">消息</param> 
    public async void PublishMesage(string topic, string mesage)
    {
        try
        {
            if (_client != null)
            {
                if (string.IsNullOrEmpty(mesage) || string.IsNullOrWhiteSpace(mesage))
                    {
                    //SLog.Loger.Warning("MQT客户端不能发布为空的消息！"); 
                    return;
                }
                MqttClientPublishResult result = await _client.PublishStringAsync(topic, mesage, MqttQualityOfServiceLevel.AtLeastOnce);//恰好一次， QoS 级别1  
                Console.WriteLine($"发布消息-主题：{topic}，消息：{mesage}，结果： {result.ReasonCode}");
            }
            else
            {
                //SLog.Loger.Warning("MQT客户端未连接服务端，不能发布主题为[{0}]的消息：{1}", topic, mesage); 
                return;
            }
        }
        catch (Exception ex)
        {
            //SLog.Loger.Eror("MQT客户端发布主题为[{0}]的消息：{1}，错误：{2}", topic, mesage, ex.Mesage); 
        }
    } 
    #endregion 
    }
}
